版本

建议安装 Microsoft R Open,对 R 中向量和矩阵的向量化操作进行了大幅优化,无需引入任何包,就能自动调用多核进行并行计算,可提高运算速度数十倍。

这也意味着,向量化运算是 R 最基本、最主要的代码风格。

但是,Microsoft R Open 的某些包更新不够及时(如 rmarkdown)。为了 Knit 的方便,可以 Rstudio 选择 R 的最新版,在 VSCode 中用 Microsoft R Open 日常加速计算。

System interaction

Environment

R 中词法作用域的机理通过环境实现。

函数优先在内部环境搜索用到的变量,搜索不到时,就到上一级环境搜索。

每加载一个扩展包,这个包的环境都会插入搜索路径,并位于全局环境之前。

环境交互函数
ls()/objects() 列出当前工作空间的所有对象
ls.str(mode=, pattern=) 显示当前环境的结构。mode 参数过滤对象类型,pattern 参数为正则表达式,过滤对象名
rm() 删除一个或更多个对象。
rm(list=ls(all=TRUE)) 删除所有对象
getOption() 查看全局选项
options() 设定全局选项。如 digits=7, 设定显示数字的位数。warn=0, 可以改为1,在警告产生时立即显示。

Path

Working Directory
dir.create() 创建一个目录
getwd()/setwd() 查看/设定当前工作目录

由此扩展,R 也可以完成文件管理工作。

Memory

tracemem()查看对象的内存地址

Time

Sys.sleep() 使程序暂停若干秒,一般用于某些循环(爬虫、动画等),故意降低运行频率

sys.time() 读取系统时间;

system.time() 参数为大括号括起来的代码段,计算代码段运行时间

t1 <- Sys.time()
...
t2 <- Sys.time()
print(t2 - t1) # 中间代码的运行时间

system.time({
  ...
}) # 代码段运行时间
A <- matrix(1:1000000, 1000)

system.time({
  A %*% A
})
#> 用户 系统 流逝 
#> 1.16 0.00 1.16

Help

Help
help(functionName)?functionName 查看一个函数的用法
example(functionName) 运行帮主文件中的例子
help.search(keyword)??keyword 在==所有包==的文档中搜索关键词
help(package = "") 查看一个包的帮助
help(options) 显示可用选项的说明
options() 显示或设置当前选项

History

History
history(#) 显示最近使用过的#个命令(默认值为25)
savehistory("myfile") 保存命令历史到文件myfile中(默认值为.Rhistory)
loadhistory("myfile") 载入一个命令历史文件(默认值为.Rhistory)

Modularization

R Package

Package
install.packages("package") 安装包
installed.packages() 返回一个字符串矩阵,内含所有已安装的包的多项信息
library() 无参数时,显示库中已经安装了哪些包
library(package) 有参数时,载入包
unloadNamespace(package) 解除载入一个包
search() 查看环境中已经加载了哪些包
getOption('defaultPackages') 查看默认加载的 R 包
update.packages("package") 升级包。若不填入参数,则自动升级所有可以升级的包
package::function() 调用相应包的函数(有时多个包用同一个名字命名不同的函数,会发生冲突,只能这样引用
data() 查看所有预先提供的数据
data(package="") 查看某个包所有预先提供的数据
data(dataset_name, package=) 读入包中数据
installed.packages() %>% View()

新装 R 时安装常用包

# 重装 R 前把已安装包的名称(第一列)存为一个 .rds 文件
saveRDS(installed.packages()[, 1], "./download-packages.rds")
# 重装 R 后读取该文件,并安装这些 packages
install.packages(readRDS("./download-packages.rds"))

查看包的流行程度

# 查看 R 包的下载次数
library(cranlogs)

fashion <- function(package_name) {
  d <- cran_downloads(
    package = package_name,
    from = "2022-01-01",
    to = "2022-03-31"
  )
  sum(d$count)
}

fashion("ggplot2")
#> [1] 6591799
fashion("tidyverse")
#> [1] 2414103
fashion("data.table")
#> [1] 2512297
fashion("plotly")
#> [1] 916778

R Script

语法

source('xxx.R')

最佳实践

由于 R 的懒加载特性,模块中的代码不会运行,故 .R 脚本文件作为模块时,不必加载配置常量和包,纯写函数即可,所有的包和配置由主文件加载。

二进制数据文件

数据处理的中间结果一般保存为 .rda 或 .rds 即可;有其他用途的数据转换结果,才有必要保存为 .csv 或 .json

二者区别:

  1. saveRDS/readRDS 仅处理单个 R 对象。但是,它们比多对象存储方法更灵活,因为还原对象的对象名称不必与存储对象时的对象名称相同
  2. .rda 不对数据进行压缩,读写速度较快;.rds 会先对文件进行压缩再保存,速度相对稍慢。

Code Style

格式化

styler 包

styler - A non-invasive source code formatter for R (lorenzwalthert.github.io)

install.packages("styler")
  • style_text() styles a string
  • style_file() styles R and Rmd files
  • style_dir() styles all R and/or Rmd files in a directory.
  • 最常用
    • RStudio Addins 菜单,styles the active file R or Rmd file, or the highlighted code.
    • 或在 console 中输入styler:::style_active_file()

Google R Style

一般性规则

  • 尽量避免使用attach(), detach()
  • Error 应该使用Stop()来抛出
  • S3 类和 S4 类的函数不要一起使用

命名

对象命名以句号.分隔,不用下划线

函数名首字母大写,驼峰式命名法,不用句号分隔

注释

注释行以#开头,后加一个空格

代码行内短注释需要在代码后面空两格,然后#,再加一个空格

对变量和函数的说明写在它们的上方(紧邻),VSCode 的 R 插件能够自动识别

总体布局与顺序

  1. 版权声明
  2. 作者信息
  3. 文件说明, 包括程序的目的,输入以及输出
  4. source() 和 library() 说明
  5. 函数定义
  6. 可执行语句, 如果有的话 (例如, print, plot)

单元测试应在另一个独立的的文件中进行

R 程序效率

Vectorization

向量化运算,即对应元素执行同样的运算,适用于向量和矩阵。

如果运算符两边的变量 size 不相同,size 较小的变量会自动循环扩展,与 size 较大的变量保持一致。返回值的 size 将和较大的变量保持一致。

在计算矩阵的哈达马积、或对向量/矩阵的所有元素执行同样的变换时,向量化运算非常方便。

R 中的运算符和函数默认支持向量化运算,这是 R 的一大特色,Microsoft R Open 还对向量和矩阵的向量化运算进行了并行优化。因此,用好向量化运算、矩阵代数,才是符合 R 风格的代码。

如计算 \(\begin{aligned}w=\frac{1}{n} \sum_{i=1}^n|x_i-\hat{m}|\end{aligned}\),其中 \(\hat{m}\) 为中位数

# 为了表明这里是向量化的并行操作,将自变量写为大写的X
mean(abs(X - median(X)))

# 由于 R 的向量化特性,这个自定义函数天然接受向量输入
f3 <- function(X) ifelse(X >= 0, 1, 0)

多用内置函数

R base 的很多内置函数是用 C 实现的,效率很高,尽量使用。

  • sum() 向量、矩阵所有元素的和,比用循环累加快得多
  • prod() 所有元素的积
  • cumsum(),返回累计和的序列
  • cumprod(),返回累计积的序列
cumsum(1:10)
#>  [1]  1  3  6 10 15 21 28 36 45 55
cumprod(1:10)
#>  [1]       1       2       6      24     120     720    5040   40320  362880
#> [10] 3628800

避免制作副本

多用 data.table 数据框在数据上就地修改

IDE

Rstudio

R Studio cheatsheet 预览:

Shortcuts

Shortcuts
Ctrl + Shift + A 选中部分行后,格式化代码
Ctrl + Alt + I Insert chunk
Alt + - 插入 <-
Ctrl + Shift + M 插入%>%
Alt + Shift + K 显示快捷键
Ctrl + Shift + N 新建脚本 .r文件
Ctrl + Enter
Ctrl + Shift + Enter
Ctrl + Alt + R
运行一行代码
运行代码块
运行全部代码
Shift + Home/End 选中光标到行首/末之间的部分
Tab / Ctrl+Space 自动补齐
输入完函数名,按tab,自动添加开括号(和闭括号)。
Ctrl + Shift + C 注释/取消注释
F1 查看帮助
Ctrl+ ↑ 在 Console 中输入“xxx”,然后按 Ctrl+ ↑。就可以列出所有输入过的以“xxx”开头的命令。

代码高亮

GitHub 上可搜索、安装 rscodeio 主题

anthonynorth/rscodeio: An RStudio theme inspired by Visual Studio Code. (github.com)

VSCode

优点

  • 鼠标悬停,即可显示变量的定义信息和函数的帮助文档(仅限 R 包中函数的官方文档和本文件中自定义函数的定义,无法显示引入模块中的自定义函数的定义),省去了查阅文档的大量时间
  • 保存(Ctrl+S)时自动格式化
  • Ctrl+Enter自动运行一行,Ctrl+Shift+Enter自动运行当前文件

配置步骤

  1. 安装 R 包 languageserver

    install.packages("languageserver")
  2. 在 VSCode 扩展商店中安装 R 插件。

    1. 安装完成后在 VSCode 设置中搜索r.rterm.option,删除--no-save,--no-restore,添加--no-site-file和 R.exe 的路径--r-binary=C:\Program Files\R\R-4.1.3\bin\R.exe
  3. 安装 Radian:一款现代的 R console,它是用 Python 编写的

    pip install radian
    1. 安装完成后在 cmd 中输入 radian 查看是否安装成功。

    2. 若出现 “cannot determine R HOME”,可能存在多个R路径(如新安装了4.1.2版本),而 Radian 无法识别。解决这个问题的方法仍然是在 VSCode 中设置 R.exe 的路径。除了搜索r.rterm.option进行修改,也可以在设置界面的右上角打开 setting.json文件直接修改:

        "r.rterm.option": [
          "--no-site-file",
          "--r-binary=C:\\Program Files\\R\\R-4.1.3\\bin\\R.exe"
        ],
  4. VSCode 中 Radian 相关设置

    1. 搜索 r.rterm.windows,将其设置为 radian.exe 的路径。在 cmd 中(powershell 不行)输入 where radian 可以获取其路径。
    2. 搜索R: Bracketed Paste并勾选,否则 Radian 不会启用
    3. 搜索r.sessionWatcher并勾选

如何在 VSCODE 中高效使用 R 语言 (图文详解)_Baimoc-CSDN博客

LS0tDQp0aXRsZTogIlIgRW5naW5lZXJpbmciDQpzdWJ0aXRsZTogJycNCmF1dGhvcjogIkh1bW9vbiINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDogaHRtbF9kb2N1bWVudA0KZG9jdW1lbnRjbGFzczogY3RleGFydA0KY2xhc3NvcHRpb246IGh5cGVycmVmLA0KLS0tDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0NCnNvdXJjZSgiLi4vUm1hcmtkb3duLXRlbXBsYXRlL1JtYXJrZG93bl9jb25maWcuUiIpDQoNCiMjIGdsb2JhbCBvcHRpb25zID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIHdpZHRoID0gY29uZmlnJHdpZHRoLA0KICBmaWcud2lkdGggPSBjb25maWckZmlnLndpZHRoLA0KICBmaWcuYXNwID0gY29uZmlnJGZpZy5hc3AsDQogIG91dC53aWR0aCA9IGNvbmZpZyRvdXQud2lkdGgsDQogIGZpZy5hbGlnbiA9IGNvbmZpZyRmaWcuYWxpZ24sDQogIGZpZy5wYXRoID0gY29uZmlnJGZpZy5wYXRoLA0KICBmaWcuc2hvdyA9IGNvbmZpZyRmaWcuc2hvdywNCiAgd2FybiA9IGNvbmZpZyR3YXJuLA0KICB3YXJuaW5nID0gY29uZmlnJHdhcm5pbmcsDQogIG1lc3NhZ2UgPSBjb25maWckbWVzc2FnZSwNCiAgZWNobyA9IGNvbmZpZyRlY2hvLA0KICBldmFsID0gY29uZmlnJGV2YWwsDQogIHRpZHkgPSBjb25maWckdGlkeSwNCiAgY29tbWVudCA9IGNvbmZpZyRjb21tZW50LA0KICBjb2xsYXBzZSA9IGNvbmZpZyRjb2xsYXBzZSwNCiAgY2FjaGUgPSBjb25maWckY2FjaGUsDQogIGNhY2hlLmNvbW1lbnRzID0gY29uZmlnJGNhY2hlLmNvbW1lbnRzLA0KICBhdXRvZGVwID0gY29uZmlnJGF1dG9kZXANCikNCmBgYA0KDQojIyDniYjmnKwNCg0K5bu66K6u5a6J6KOFIE1pY3Jvc29mdCBSIE9wZW7vvIzlr7kgUiDkuK3lkJHph4/lkoznn6npmLXnmoTlkJHph4/ljJbmk43kvZzov5vooYzkuoblpKfluYXkvJjljJbvvIzml6DpnIDlvJXlhaXku7vkvZXljIXvvIzlsLHog73oh6rliqjosIPnlKjlpJrmoLjov5vooYzlubbooYzorqHnrpfvvIzlj6/mj5Dpq5jov5DnrpfpgJ/luqbmlbDljYHlgI3jgIINCg0K6L+Z5Lmf5oSP5ZGz552A77yM5ZCR6YeP5YyW6L+Q566X5pivIFIg5pyA5Z+65pys44CB5pyA5Li76KaB55qE5Luj56CB6aOO5qC844CCDQoNCuS9huaYr++8jE1pY3Jvc29mdCBSIE9wZW4g55qE5p+Q5Lqb5YyF5pu05paw5LiN5aSf5Y+K5pe277yI5aaCIHJtYXJrZG93bu+8ieOAguS4uuS6hiBLbml0IOeahOaWueS+v++8jOWPr+S7pSBSc3R1ZGlvIOmAieaLqSBSIOeahOacgOaWsOeJiO+8jOWcqCBWU0NvZGUg5Lit55SoIE1pY3Jvc29mdCBSIE9wZW4g5pel5bi45Yqg6YCf6K6h566X44CCDQoNCiMjIFN5c3RlbSBpbnRlcmFjdGlvbg0KDQojIyMgRW52aXJvbm1lbnQNCg0KUiDkuK3or43ms5XkvZznlKjln5/nmoTmnLrnkIbpgJrov4fnjq/looPlrp7njrDjgIINCg0K5Ye95pWw5LyY5YWI5Zyo5YaF6YOo546v5aKD5pCc57Si55So5Yiw55qE5Y+Y6YeP77yM5pCc57Si5LiN5Yiw5pe277yM5bCx5Yiw5LiK5LiA57qn546v5aKD5pCc57Si44CCDQoNCuavj+WKoOi9veS4gOS4quaJqeWxleWMhe+8jOi/meS4quWMheeahOeOr+Wig+mDveS8muaPkuWFpeaQnOe0oui3r+W+hO+8jOW5tuS9jeS6juWFqOWxgOeOr+Wig+S5i+WJjeOAgg0KDQp8IOeOr+Wig+S6pOS6kuWHveaVsCAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHwNCnwgYGxzKClgL2BvYmplY3RzKClgICAgICAgICB8IOWIl+WHuuW9k+WJjeW3peS9nOepuumXtOeahOaJgOacieWvueixoSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBgbHMuc3RyKG1vZGU9LCBwYXR0ZXJuPSlgIHwg5pi+56S65b2T5YmN546v5aKD55qE57uT5p6E44CCbW9kZSDlj4LmlbDov4fmu6Tlr7nosaHnsbvlnovvvIxwYXR0ZXJuIOWPguaVsOS4uuato+WImeihqOi+vuW8j++8jOi/h+a7pOWvueixoeWQjSB8DQp8IGBybSgpYCAgICAgICAgICAgICAgICAgICAgfCDliKDpmaTkuIDkuKrmiJbmm7TlpJrkuKrlr7nosaHjgIIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGBybShsaXN0PWxzKGFsbD1UUlVFKSlgICAgfCDliKDpmaTmiYDmnInlr7nosaEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBgZ2V0T3B0aW9uKClgICAgICAgICAgICAgIHwg5p+l55yL5YWo5bGA6YCJ6aG5ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYG9wdGlvbnMoKWAgICAgICAgICAgICAgICB8IOiuvuWumuWFqOWxgOmAiemhueOAguWmgiBkaWdpdHM9Nywg6K6+5a6a5pi+56S65pWw5a2X55qE5L2N5pWw44CCd2Fybj0wLCDlj6/ku6XmlLnkuLox77yM5Zyo6K2m5ZGK5Lqn55Sf5pe256uL5Y2z5pi+56S644CCIHwNCg0KIyMjIFBhdGgNCg0KfCBXb3JraW5nICBEaXJlY3RvcnkgIHwgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgLS0tLS0tLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8DQp8IGBkaXIuY3JlYXRlKClgICAgICAgfCDliJvlu7rkuIDkuKrnm67lvZUgICAgICAgICAgfA0KfCBgZ2V0d2QoKWAvYHNldHdkKClgIHwg5p+l55yLL+iuvuWumuW9k+WJjeW3peS9nOebruW9lSB8DQoNCueUseatpOaJqeWxle+8jFIg5Lmf5Y+v5Lul5a6M5oiQ5paH5Lu2566h55CG5bel5L2c44CCDQoNCiMjIyBNZW1vcnkNCg0KYHRyYWNlbWVtKClg5p+l55yL5a+56LGh55qE5YaF5a2Y5Zyw5Z2ADQoNCiMjIyBUaW1lDQoNCmBTeXMuc2xlZXAoKWAg5L2/56iL5bqP5pqC5YGc6Iul5bmy56eS77yM5LiA6Iis55So5LqO5p+Q5Lqb5b6q546v77yI54is6Jmr44CB5Yqo55S7562J77yJ77yM5pWF5oSP6ZmN5L2O6L+Q6KGM6aKR546HDQoNCmBzeXMudGltZSgpYCDor7vlj5bns7vnu5/ml7bpl7TvvJsNCg0KYHN5c3RlbS50aW1lKClgIOWPguaVsOS4uuWkp+aLrOWPt+aLrOi1t+adpeeahOS7o+eggeaute+8jOiuoeeul+S7o+eggeautei/kOihjOaXtumXtA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnQxIDwtIFN5cy50aW1lKCkNCi4uLg0KdDIgPC0gU3lzLnRpbWUoKQ0KcHJpbnQodDIgLSB0MSkgIyDkuK3pl7Tku6PnoIHnmoTov5DooYzml7bpl7QNCg0Kc3lzdGVtLnRpbWUoew0KICAuLi4NCn0pICMg5Luj56CB5q616L+Q6KGM5pe26Ze0DQpgYGANCg0KYGBge3J9DQpBIDwtIG1hdHJpeCgxOjEwMDAwMDAsIDEwMDApDQoNCnN5c3RlbS50aW1lKHsNCiAgQSAlKiUgQQ0KfSkNCmBgYA0KDQojIyMgSGVscA0KDQp8IEhlbHAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHwNCnwgYGhlbHAoZnVuY3Rpb25OYW1lKWDmiJZgP2Z1bmN0aW9uTmFtZWAgfCDmn6XnnIvkuIDkuKrlh73mlbDnmoTnlKjms5UgICAgICAgICAgICAgfA0KfCBgZXhhbXBsZShmdW5jdGlvbk5hbWUpYCAgICAgICAgICAgICAgIHwg6L+Q6KGM5biu5Li75paH5Lu25Lit55qE5L6L5a2QICAgICAgICAgICB8DQp8IGBoZWxwLnNlYXJjaChrZXl3b3JkKWDmiJZgPz9rZXl3b3JkYCAgIHwg5ZyoPT3miYDmnInljIU9PeeahOaWh+aho+S4reaQnOe0ouWFs+mUruivjSB8DQp8IGBoZWxwKHBhY2thZ2UgPSAiIilgICAgICAgICAgICAgICAgICAgfCDmn6XnnIvkuIDkuKrljIXnmoTluK7liqkgICAgICAgICAgICAgICB8DQp8IGBoZWxwKG9wdGlvbnMpYCAgICAgICAgICAgICAgICAgICAgICAgfCDmmL7npLrlj6/nlKjpgInpobnnmoTor7TmmI4gICAgICAgICAgICAgfA0KfCBgb3B0aW9ucygpYCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5pi+56S65oiW6K6+572u5b2T5YmN6YCJ6aG5ICAgICAgICAgICAgIHwNCg0KIyMjIEhpc3RvcnkNCg0KfCBIaXN0b3J5ICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8DQp8IGBoaXN0b3J5KCMpYCAgICAgICAgICAgIHwg5pi+56S65pyA6L+R5L2/55So6L+H55qEI+S4quWRveS7pO+8iOm7mOiupOWAvOS4ujI177yJICAgICAgICAgICB8DQp8IGBzYXZlaGlzdG9yeSgibXlmaWxlIilgIHwg5L+d5a2Y5ZG95Luk5Y6G5Y+y5Yiw5paH5Lu2bXlmaWxl5Lit77yI6buY6K6k5YC85Li6LlJoaXN0b3J577yJIHwNCnwgYGxvYWRoaXN0b3J5KCJteWZpbGUiKWAgfCDovb3lhaXkuIDkuKrlkb3ku6Tljoblj7Lmlofku7bvvIjpu5jorqTlgLzkuLouUmhpc3RvcnnvvIkgICAgICAgfA0KDQoNCg0KIyMgTW9kdWxhcml6YXRpb24NCg0KIyMjIFIgUGFja2FnZQ0KDQp8IFBhY2thZ2UgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8DQp8IGBpbnN0YWxsLnBhY2thZ2VzKCJwYWNrYWdlIilgICB8IOWuieijheWMhSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGBpbnN0YWxsZWQucGFja2FnZXMoKWAgICAgICAgICB8IOi/lOWbnuS4gOS4quWtl+espuS4suefqemYte+8jOWGheWQq+aJgOacieW3suWuieijheeahOWMheeahOWkmumhueS/oeaBryAgICAgICAgICAgICAgICAgfA0KfCBgbGlicmFyeSgpYCAgICAgICAgICAgICAgICAgICAgfCDml6Dlj4LmlbDml7bvvIzmmL7npLrlupPkuK3lt7Lnu4/lronoo4Xkuoblk6rkupvljIUgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGBsaWJyYXJ5KHBhY2thZ2UpYCAgICAgICAgICAgICB8IOacieWPguaVsOaXtu+8jOi9veWFpeWMhSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYHVubG9hZE5hbWVzcGFjZShwYWNrYWdlKWAgICAgIHwg6Kej6Zmk6L295YWl5LiA5Liq5YyFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGBzZWFyY2goKWAgICAgICAgICAgICAgICAgICAgICB8IOafpeeci+eOr+Wig+S4reW3sue7j+WKoOi9veS6huWTquS6m+WMhSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBgZ2V0T3B0aW9uKCdkZWZhdWx0UGFja2FnZXMnKWAgfCDmn6XnnIvpu5jorqTliqDovb3nmoQgUiDljIUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGB1cGRhdGUucGFja2FnZXMoInBhY2thZ2UiKWAgICB8IOWNh+e6p+WMheOAguiLpeS4jeWhq+WFpeWPguaVsO+8jOWImeiHquWKqOWNh+e6p+aJgOacieWPr+S7peWNh+e6p+eahOWMhSAgICAgICAgICAgICB8DQp8IGBwYWNrYWdlOjpmdW5jdGlvbigpYCAgICAgICAgICB8IOiwg+eUqOebuOW6lOWMheeahOWHveaVsO+8iOacieaXtioq5aSa5Liq5YyF55So5ZCM5LiA5Liq5ZCN5a2X5ZG95ZCN5LiN5ZCM55qE5Ye95pWw77yM5Lya5Y+R55Sf5Yay56qB77yM5Y+q6IO96L+Z5qC35byV55SoKirvvIkgfA0KfCBgZGF0YSgpYCAgICAgICAgICAgICAgICAgICAgICAgfCDmn6XnnIvmiYDmnInpooTlhYjmj5DkvpvnmoTmlbDmja4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGBkYXRhKHBhY2thZ2U9IiIpYCAgICAgICAgICAgICB8IOafpeeci+afkOS4quWMheaJgOaciemihOWFiOaPkOS+m+eahOaVsOaNriAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGRhdGEoZGF0YXNldF9uYW1lLCBwYWNrYWdlPSlgIHwg6K+75YWl5YyF5Lit5pWw5o2uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQppbnN0YWxsZWQucGFja2FnZXMoKSAlPiUgVmlldygpDQpgYGANCg0KIVtdKGh0dHA6Ly9odW1vb24taW1hZ2UtaG9zdGluZy1zZXJ2aWNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS9pbWcvdHlwb3JhLzIwMjIvaW1hZ2UtMjAyMjA0MDQwNTUzMjUwOTkucG5nKQ0KDQojIyMjIOaWsOijhSBSIOaXtuWuieijheW4uOeUqOWMhQ0KDQpgYGB7cn0NCiMg6YeN6KOFIFIg5YmN5oqK5bey5a6J6KOF5YyF55qE5ZCN56ew77yI56ys5LiA5YiX77yJ5a2Y5Li65LiA5LiqIC5yZHMg5paH5Lu2DQpzYXZlUkRTKGluc3RhbGxlZC5wYWNrYWdlcygpWywgMV0sICIuL2Rvd25sb2FkLXBhY2thZ2VzLnJkcyIpDQpgYGANCg0KYGBgUg0KIyDph43oo4UgUiDlkI7or7vlj5bor6Xmlofku7bvvIzlubblronoo4Xov5nkupsgcGFja2FnZXMNCmluc3RhbGwucGFja2FnZXMocmVhZFJEUygiLi9kb3dubG9hZC1wYWNrYWdlcy5yZHMiKSkNCmBgYA0KDQojIyMjIOafpeeci+WMheeahOa1geihjOeoi+W6pg0KDQpgYGB7cn0NCiMg5p+l55yLIFIg5YyF55qE5LiL6L295qyh5pWwDQpsaWJyYXJ5KGNyYW5sb2dzKQ0KDQpmYXNoaW9uIDwtIGZ1bmN0aW9uKHBhY2thZ2VfbmFtZSkgew0KICBkIDwtIGNyYW5fZG93bmxvYWRzKA0KICAgIHBhY2thZ2UgPSBwYWNrYWdlX25hbWUsDQogICAgZnJvbSA9ICIyMDIyLTAxLTAxIiwNCiAgICB0byA9ICIyMDIyLTAzLTMxIg0KICApDQogIHN1bShkJGNvdW50KQ0KfQ0KDQpmYXNoaW9uKCJnZ3Bsb3QyIikNCmZhc2hpb24oInRpZHl2ZXJzZSIpDQpmYXNoaW9uKCJkYXRhLnRhYmxlIikNCmZhc2hpb24oInBsb3RseSIpDQpgYGANCg0KIyMjIFIgU2NyaXB0DQoNCiMjIyMg6K+t5rOVDQoNCmBzb3VyY2UoJ3h4eC5SJylgIA0KDQojIyMjIOacgOS9s+Wunui3tQ0KDQrnlLHkuo4gUiDnmoTmh5LliqDovb3nibnmgKfvvIzmqKHlnZfkuK3nmoTku6PnoIHkuI3kvJrov5DooYzvvIzmlYUgLlIg6ISa5pys5paH5Lu25L2c5Li65qih5Z2X5pe277yM5LiN5b+F5Yqg6L296YWN572u5bi46YeP5ZKM5YyF77yM57qv5YaZ5Ye95pWw5Y2z5Y+v77yM5omA5pyJ55qE5YyF5ZKM6YWN572u55Sx5Li75paH5Lu25Yqg6L2944CCDQoNCiMjIyDkuozov5vliLbmlbDmja7mlofku7YNCg0K5pWw5o2u5aSE55CG55qE5Lit6Ze057uT5p6c5LiA6Iis5L+d5a2Y5Li6IC5yZGEg5oiWIC5yZHMg5Y2z5Y+v77yb5pyJ5YW25LuW55So6YCU55qE5pWw5o2u6L2s5o2i57uT5p6c77yM5omN5pyJ5b+F6KaB5L+d5a2Y5Li6IC5jc3Yg5oiWIC5qc29uDQoNCuS6jOiAheWMuuWIq++8mg0KDQoxLiBzYXZlUkRTL3JlYWRSRFMg5LuF5aSE55CG5Y2V5LiqIFIg5a+56LGh44CC5L2G5piv77yM5a6D5Lus5q+U5aSa5a+56LGh5a2Y5YKo5pa55rOV5pu054G15rS777yM5Zug5Li66L+Y5Y6f5a+56LGh55qE5a+56LGh5ZCN56ew5LiN5b+F5LiO5a2Y5YKo5a+56LGh5pe255qE5a+56LGh5ZCN56ew55u45ZCMDQoyLiAucmRhIOS4jeWvueaVsOaNrui/m+ihjOWOi+e8qe+8jOivu+WGmemAn+W6pui+g+W/q++8my5yZHMg5Lya5YWI5a+55paH5Lu26L+b6KGM5Y6L57yp5YaN5L+d5a2Y77yM6YCf5bqm55u45a+556iN5oWi44CCDQoNCiMjIENvZGUgU3R5bGUNCg0KIyMjIOagvOW8j+WMlg0KDQojIyMjIHN0eWxlciDljIUNCg0KW3N0eWxlciAtIEEgbm9uLWludmFzaXZlIHNvdXJjZSBjb2RlIGZvcm1hdHRlciBmb3IgUiAobG9yZW56d2FsdGhlcnQuZ2l0aHViLmlvKV0oaHR0cHM6Ly9sb3Jlbnp3YWx0aGVydC5naXRodWIuaW8vc3R5bGVycG9zdC8pDQoNCmBgYFINCmluc3RhbGwucGFja2FnZXMoInN0eWxlciIpDQpgYGANCg0KLSBgc3R5bGVfdGV4dCgpYCBzdHlsZXMgYSBzdHJpbmcNCi0gYHN0eWxlX2ZpbGUoKWAgc3R5bGVzIFIgYW5kIFJtZCBmaWxlcw0KLSBgc3R5bGVfZGlyKClgIHN0eWxlcyBhbGwgUiBhbmQvb3IgUm1kIGZpbGVzIGluIGEgZGlyZWN0b3J5Lg0KLSAqKuacgOW4uOeUqCoqDQogIC0gUlN0dWRpbyBgQWRkaW5zYCDoj5zljZXvvIxzdHlsZXMgdGhlIGFjdGl2ZSBmaWxlIFIgb3IgUm1kIGZpbGUsIG9yIHRoZSBoaWdobGlnaHRlZCBjb2RlLg0KICAtIOaIluWcqCBjb25zb2xlIOS4rei+k+WFpWBzdHlsZXI6OjpzdHlsZV9hY3RpdmVfZmlsZSgpYA0KICANCg0KIyMjIEdvb2dsZSBSIFN0eWxlDQoNCiMjIyMg5LiA6Iis5oCn6KeE5YiZDQoNCi0g5bC96YeP6YG/5YWN5L2/55SoYGF0dGFjaCgpYCwgYGRldGFjaCgpYA0KLSBFcnJvciDlupTor6Xkvb/nlKhgU3RvcCgpYOadpeaKm+WHug0KLSBTMyDnsbvlkowgUzQg57G755qE5Ye95pWw5LiN6KaB5LiA6LW35L2/55SoDQoNCiMjIyMg5ZG95ZCNDQoNCuWvueixoeWRveWQjeS7peWPpeWPt2AuYOWIhumalO+8jOS4jeeUqOS4i+WIkue6vw0KDQrlh73mlbDlkI3pppblrZfmr43lpKflhpnvvIzpqbzls7DlvI/lkb3lkI3ms5XvvIzkuI3nlKjlj6Xlj7fliIbpmpQNCg0KIyMjIyDms6jph4oNCg0K5rOo6YeK6KGM5LulYCNg5byA5aS077yM5ZCO5Yqg5LiA5Liq56m65qC8DQoNCuS7o+eggeihjOWGheefreazqOmHiumcgOimgeWcqOS7o+eggeWQjumdouepuuS4pOagvO+8jOeEtuWQjmAjYO+8jOWGjeWKoOS4gOS4quepuuagvA0KDQoqKuWvueWPmOmHj+WSjOWHveaVsOeahOivtOaYjuWGmeWcqOWug+S7rOeahOS4iuaWue+8iOe0p+mCu++8ie+8jFZTQ29kZSDnmoQgUiDmj5Lku7bog73lpJ/oh6rliqjor4bliKsqKg0KDQojIyMjIOaAu+S9k+W4g+WxgOS4jumhuuW6jw0KDQoxLiDniYjmnYPlo7DmmI4NCjIuIOS9nOiAheS/oeaBrw0KMy4g5paH5Lu26K+05piOLCDljIXmi6znqIvluo/nmoTnm67nmoTvvIzovpPlhaXku6Xlj4rovpPlh7oNCjQuIHNvdXJjZSgpIOWSjCBsaWJyYXJ5KCkg6K+05piODQo1LiDlh73mlbDlrprkuYkNCjYuIOWPr+aJp+ihjOivreWPpSwg5aaC5p6c5pyJ55qE6K+dICjkvovlpoIsIHByaW50LCBwbG90KQ0KDQrljZXlhYPmtYvor5XlupTlnKjlj6bkuIDkuKrni6znq4vnmoTnmoTmlofku7bkuK3ov5vooYwNCg0KIyMgUiDnqIvluo/mlYjnjocNCg0KIyMjIFZlY3Rvcml6YXRpb24NCg0K5ZCR6YeP5YyW6L+Q566X77yM5Y2z5a+55bqU5YWD57Sg5omn6KGM5ZCM5qC355qE6L+Q566X77yM6YCC55So5LqO5ZCR6YeP5ZKM55+p6Zi144CCDQoNCuWmguaenOi/kOeul+espuS4pOi+ueeahOWPmOmHjyBzaXplIOS4jeebuOWQjO+8jHNpemUg6L6D5bCP55qE5Y+Y6YeP5Lya6Ieq5Yqo5b6q546v5omp5bGV77yM5LiOIHNpemUg6L6D5aSn55qE5Y+Y6YeP5L+d5oyB5LiA6Ie044CC6L+U5Zue5YC855qEIHNpemUg5bCG5ZKM6L6D5aSn55qE5Y+Y6YeP5L+d5oyB5LiA6Ie044CCDQoNCuWcqOiuoeeul+efqemYteeahOWTiOi+vumprOenr+OAgeaIluWvueWQkemHjy/nn6npmLXnmoTmiYDmnInlhYPntKDmiafooYzlkIzmoLfnmoTlj5jmjaLml7bvvIzlkJHph4/ljJbov5DnrpfpnZ7luLjmlrnkvr/jgIINCg0KUiDkuK3nmoTov5DnrpfnrKblkozlh73mlbDpu5jorqTmlK/mjIHlkJHph4/ljJbov5DnrpfvvIzov5nmmK8gUiDnmoTkuIDlpKfnibnoibLvvIxNaWNyb3NvZnQgUiBPcGVuIOi/mOWvueWQkemHj+WSjOefqemYteeahOWQkemHj+WMlui/kOeul+i/m+ihjOS6huW5tuihjOS8mOWMluOAguWboOatpO+8jOeUqOWlveWQkemHj+WMlui/kOeul+OAgeefqemYteS7o+aVsO+8jOaJjeaYr+espuWQiCBSIOmjjuagvOeahOS7o+eggeOAgg0KDQrlpoLorqHnrpcgJFxiZWdpbnthbGlnbmVkfXc9XGZyYWN7MX17bn0gXHN1bV97aT0xfV5ufHhfaS1caGF0e219fFxlbmR7YWxpZ25lZH0k77yM5YW25LitICRcaGF0e219JCDkuLrkuK3kvY3mlbANCg0KYGBge3IsIGV2YWw9RkFMU0V9DQojIOS4uuS6huihqOaYjui/memHjOaYr+WQkemHj+WMlueahOW5tuihjOaTjeS9nO+8jOWwhuiHquWPmOmHj+WGmeS4uuWkp+WGmeeahFgNCm1lYW4oYWJzKFggLSBtZWRpYW4oWCkpKQ0KDQojIOeUseS6jiBSIOeahOWQkemHj+WMlueJueaAp++8jOi/meS4quiHquWumuS5ieWHveaVsOWkqeeEtuaOpeWPl+WQkemHj+i+k+WFpQ0KZjMgPC0gZnVuY3Rpb24oWCkgaWZlbHNlKFggPj0gMCwgMSwgMCkNCmBgYA0KDQojIyMg5aSa55So5YaF572u5Ye95pWwDQoNClIgYmFzZSDnmoTlvojlpJrlhoXnva7lh73mlbDmmK/nlKggQyDlrp7njrDnmoTvvIzmlYjnjoflvojpq5jvvIzlsL3ph4/kvb/nlKjjgIINCg0KLSBzdW0oKSDlkJHph4/jgIHnn6npmLXmiYDmnInlhYPntKDnmoTlkozvvIzmr5TnlKjlvqrnjq/ntK/liqDlv6vlvpflpJoNCi0gcHJvZCgpIOaJgOacieWFg+e0oOeahOenrw0KLSBjdW1zdW0oKe+8jOi/lOWbnue0r+iuoeWSjOeahOW6j+WIlw0KLSBjdW1wcm9kKCnvvIzov5Tlm57ntK/orqHnp6/nmoTluo/liJcNCg0KYGBge3J9DQpjdW1zdW0oMToxMCkNCmN1bXByb2QoMToxMCkNCmBgYA0KDQojIyMg6YG/5YWN5Yi25L2c5Ymv5pysDQoNCuWkmueUqCBkYXRhLnRhYmxlIOaVsOaNruahhuWcqOaVsOaNruS4iuWwseWcsOS/ruaUuQ0KDQojIyBJREUNCg0KIyMjIFJzdHVkaW8NCg0KW1IgU3R1ZGlvIGNoZWF0c2hlZXRdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL21haW4vcnN0dWRpby1pZGUucGRmKSDpooTop4jvvJoNCg0KPG9iamVjdCBkYXRhPSIuLi9wZGYvY2hlYXRzaGVldC1yc3R1ZGlvLWlkZS5wZGYiIHR5cGU9ImFwcGxpY2F0aW9uL3BkZiIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSI+PC9vYmplY3Q+DQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KZG93bmxvYWR0aGlzOjpkb3dubG9hZF9maWxlKA0KICBwYXRoID0gIi4uL3BkZi9jaGVhdHNoZWV0LXJzdHVkaW8taWRlLnBkZiIsDQogIG91dHB1dF9uYW1lID0gImNoZWF0c2hlZXQtcnN0dWRpby1pZGUiLA0KICBidXR0b25fbGFiZWwgPSAiRG93bmxvYWQgY2hlYXRzaGVldCIsDQogIGJ1dHRvbl90eXBlID0gInN1Y2Nlc3MiLA0KICBzZWxmX2NvbnRhaW5lZCA9IEZBTFNFDQopDQpgYGANCg0KIyMjIyBTaG9ydGN1dHMNCg0KfCBTaG9ydGN1dHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8DQp8IEN0cmwgKyBTaGlmdCArIEEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDpgInkuK3pg6jliIbooYzlkI7vvIzmoLzlvI/ljJbku6PnoIEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBDdHJsICsgQWx0ICsgSSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgSW5zZXJ0IGNodW5rICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQWx0ICsgLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOaPkuWFpSA8LSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQ3RybCArIFNoaWZ0ICsgTSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOaPkuWFpSU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQWx0ICsgU2hpZnQgKyBLICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOaYvuekuuW/q+aNt+mUriAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQ3RybCArIFNoaWZ0ICsgTiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOaWsOW7uuiEmuacrCAucuaWh+S7tiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IEN0cmwgKyBFbnRlcjxiciAvPkN0cmwgKyBTaGlmdCArIEVudGVyPGJyIC8+Q3RybCArIEFsdCArIFIgfCDov5DooYzkuIDooYzku6PnoIE8YnIgLz7ov5DooYzku6PnoIHlnZc8YnIgLz7ov5DooYzlhajpg6jku6PnoIEgICAgICAgICAgICAgICB8DQp8IFNoaWZ0ICsgSG9tZS9FbmQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDpgInkuK3lhYnmoIfliLDooYzpppYv5pyr5LmL6Ze055qE6YOo5YiGICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgVGFiIC8gQ3RybCtTcGFjZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOiHquWKqOihpem9kDxiciAvPui+k+WFpeWujOWHveaVsOWQje+8jOaMiXRhYu+8jOiHquWKqOa3u+WKoOW8gOaLrOWPtyjlkozpl63mi6zlj7cp44CCIHwNCnwgQ3RybCArIFNoaWZ0ICsgQyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOazqOmHii/lj5bmtojms6jph4ogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IEYxICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDmn6XnnIvluK7liqkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQ3RybCsg4oaRICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5ZyoIENvbnNvbGUg5Lit6L6T5YWl4oCceHh44oCd77yM54S25ZCO5oyJIEN0cmwrIOKGkeOAguWwseWPr+S7peWIl+WHuuaJgOaciei+k+WFpei/h+eahOS7peKAnHh4eOKAneW8gOWktOeahOWRveS7pOOAgiB8DQoNCiMjIyMg5Luj56CB6auY5LquDQoNCkdpdEh1YiDkuIrlj6/mkJzntKLjgIHlronoo4UgcnNjb2RlaW8g5Li76aKYDQoNClthbnRob255bm9ydGgvcnNjb2RlaW86IEFuIFJTdHVkaW8gdGhlbWUgaW5zcGlyZWQgYnkgVmlzdWFsIFN0dWRpbyBDb2RlLiAoZ2l0aHViLmNvbSldKGh0dHBzOi8vZ2l0aHViLmNvbS9hbnRob255bm9ydGgvcnNjb2RlaW8pDQoNCiMjIyBWU0NvZGUNCg0KIyMjIyDkvJjngrkNCg0KLSDpvKDmoIfmgqzlgZzvvIzljbPlj6/mmL7npLrlj5jph4/nmoTlrprkuYnkv6Hmga/lkozlh73mlbDnmoTluK7liqnmlofmoaPvvIjku4XpmZAgUiDljIXkuK3lh73mlbDnmoTlrpjmlrnmlofmoaPlkozmnKzmlofku7bkuK3oh6rlrprkuYnlh73mlbDnmoTlrprkuYnvvIzml6Dms5XmmL7npLrlvJXlhaXmqKHlnZfkuK3nmoToh6rlrprkuYnlh73mlbDnmoTlrprkuYnvvInvvIznnIHljrvkuobmn6XpmIXmlofmoaPnmoTlpKfph4/ml7bpl7QNCi0g5L+d5a2Y77yIYEN0cmwrU2DvvInml7boh6rliqjmoLzlvI/ljJYNCi0gYEN0cmwrRW50ZXJg6Ieq5Yqo6L+Q6KGM5LiA6KGM77yMYEN0cmwrU2hpZnQrRW50ZXJg6Ieq5Yqo6L+Q6KGM5b2T5YmN5paH5Lu2DQoNCiMjIyMg6YWN572u5q2l6aqkDQoNCjEuIOWuieijhSBSIOWMhSBsYW5ndWFnZXNlcnZlcg0KDQogICBgYGBSDQogICBpbnN0YWxsLnBhY2thZ2VzKCJsYW5ndWFnZXNlcnZlciIpDQogICBgYGANCg0KMi4g5ZyoIFZTQ29kZSDmianlsZXllYblupfkuK3lronoo4UgUiDmj5Lku7bjgIINCg0KICAgMS4g5a6J6KOF5a6M5oiQ5ZCO5ZyoIFZTQ29kZSDorr7nva7kuK3mkJzntKJgci5ydGVybS5vcHRpb25g77yM5Yig6ZmkYC0tbm8tc2F2ZSwtLW5vLXJlc3RvcmVg77yM5re75YqgYC0tbm8tc2l0ZS1maWxlYOWSjCBSLmV4ZSDnmoTot6/lvoRgLS1yLWJpbmFyeT1DOlxQcm9ncmFtIEZpbGVzXFJcUi00LjEuM1xiaW5cUi5leGVgDQoNCjMuIOWuieijhSBSYWRpYW7vvJrkuIDmrL7njrDku6PnmoQgUiBjb25zb2xl77yM5a6D5piv55SoIFB5dGhvbiDnvJblhpnnmoQNCg0KICAgYGBgcG93ZXJzaGVsbA0KICAgcGlwIGluc3RhbGwgcmFkaWFuDQogICBgYGANCg0KICAgMS4g5a6J6KOF5a6M5oiQ5ZCO5ZyoIGNtZCDkuK3ovpPlhaUgYHJhZGlhbmAg5p+l55yL5piv5ZCm5a6J6KOF5oiQ5Yqf44CCDQoNCiAgIDIuIOiLpeWHuueOsCDigJxjYW5ub3QgZGV0ZXJtaW5lIFIgSE9NReKAne+8jOWPr+iDveWtmOWcqOWkmuS4qlLot6/lvoTvvIjlpoLmlrDlronoo4XkuoY0LjEuMueJiOacrO+8ie+8jOiAjCBSYWRpYW4g5peg5rOV6K+G5Yir44CC6Kej5Yaz6L+Z5Liq6Zeu6aKY55qE5pa55rOV5LuN54S25piv5ZyoIFZTQ29kZSDkuK3orr7nva4gUi5leGUg55qE6Lev5b6E44CC6Zmk5LqG5pCc57SiYHIucnRlcm0ub3B0aW9uYOi/m+ihjOS/ruaUue+8jOS5n+WPr+S7peWcqOiuvue9rueVjOmdoueahOWPs+S4iuinkuaJk+W8gCBzZXR0aW5nLmpzb27mlofku7bnm7TmjqXkv67mlLnvvJoNCg0KICAgICAgYGBgYGpzb24NCiAgICAgICAgInIucnRlcm0ub3B0aW9uIjogWw0KICAgICAgICAgICItLW5vLXNpdGUtZmlsZSIsDQogICAgICAgICAgIi0tci1iaW5hcnk9QzpcXFByb2dyYW0gRmlsZXNcXFJcXFItNC4xLjNcXGJpblxcUi5leGUiDQogICAgICAgIF0sDQogICAgICBgYGBgDQoNCjQuIFZTQ29kZSDkuK0gUmFkaWFuIOebuOWFs+iuvue9rg0KDQogICAxLiDmkJzntKIgYHIucnRlcm0ud2luZG93c2DvvIzlsIblhbborr7nva7kuLogcmFkaWFuLmV4ZSDnmoTot6/lvoTjgILlnKggY21kIOS4re+8iHBvd2Vyc2hlbGwg5LiN6KGM77yJ6L6T5YWlIGB3aGVyZSByYWRpYW5gIOWPr+S7peiOt+WPluWFtui3r+W+hOOAgg0KICAgMi4g5pCc57SiYFI6IEJyYWNrZXRlZCBQYXN0ZWDlubbli77pgInvvIzlkKbliJkgUmFkaWFuIOS4jeS8muWQr+eUqA0KICAgMy4g5pCc57SiYHIuc2Vzc2lvbldhdGNoZXJg5bm25Yu+6YCJDQoNCg0KDQpb5aaC5L2V5ZyoIFZTQ09ERSDkuK3pq5jmlYjkvb/nlKggUiDor63oqIAg77yI5Zu+5paH6K+m6Kej77yJX0JhaW1vYy1DU0RO5Y2a5a6iXShodHRwczovL2Jsb2cuY3Nkbi5uZXQvdTAxMTI2MjI1My9hcnRpY2xlL2RldGFpbHMvMTEzODM3NzIwKQ0K